//Random Game Engine
//Copyright (C) 2013  Nikolay Dionisov

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.



#ifndef __TASK_MANAGER__H_INCLUDED__
#define __TASK_MANAGER__H_INCLUDED__

#include "..\ManagerInterfaces.h"
#include "..\..\SystemInterface.h"
#include "..\..\Utilities\ThreadSafePrimitives.h"
#include <list>
#include <memory>
#include <mutex>
#include <condition_variable>
#include <map>
namespace re
{
	namespace core
	{
		typedef unsigned short TaskId;
		typedef unsigned char SessionId;

		class TaskManager;
		class Task
		{
		public:
			Task() : id_(0), parent_(0), dependency_(0), references_(1) {}
			Task(TaskId id, TaskId parent, TaskId dep, WorkPtr w);
			~Task() {}
			friend class TaskManager;
			//Avoid dynamic linking, pass directly TaskManager
			void Execute(TaskManager * const taskman);
			unsigned short GetReferences();
		protected:
			TaskId id_;
			TaskId parent_;
			TaskId dependency_;
			unsigned short references_;
			WorkPtr work_;
		};

		typedef std::map<TaskId, Task> TaskMap;
		typedef std::list<TaskId> TaskList;
		typedef std::map<SessionId, TaskList> SessionMap;
		class TaskManager : public ITaskManager
		{
		public:
			TaskManager() { last_taskid = 1; last_sessionid = 1; }
			~TaskManager() {}
			friend class Task;
			//add task in the waiting list.(it wont be executed until submitted/approved)
			TaskId AddTask(WorkPtr work, const SessionId session);
			void SetParent(const SessionId session, const TaskId parent, const TaskId child);
			void SetDependency(const SessionId session, const TaskId to_exec_first, const TaskId to_exec_second);
			//Begin the builing of a task tree.
			//Require session mutex. WARNING StartSession MUST be ended with FinishSession()
			SessionId StartSession();
			//Finish the session(send the session to the exec queue)
			//Require task and session mutexes(should be calles ONLY once per update)
			void FinishSession(SessionId id);

			bool IsCompleted(TaskId id);

			//If there is task -> Do. else wait until there is a task 
			//Require task_mutex
			void WaitDoTask();
			//If there is task -> Do. else quit
			//Require task_mutex
			bool TryDoTask();
		protected:
			//this does not require protection, since its a callback and locks are already set
			//if something get locked here, this would result in deadlock
			void TaskCompleted(TaskId id);
		private:
			TaskId GenerateTaskId();
			SessionId GenerateSessionId();
			void SortByDependency(std::vector<Task*>& vec);
			TaskId last_taskid;
			SessionId last_sessionid;
			bool UnsafeIsCompleted(TaskId task_id);

			TaskMap tasks;

			SessionMap sessions;
			mutable std::mutex task_mutex;
			mutable std::mutex session_mutex;

			mutable std::mutex opened_tasks_mutex;
			std::condition_variable condition_var;
			std::list<TaskId> opened_tasks;
		};
	}

}

#endif